/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙，邂逅框架梦]
 * 
 * https://zhiqim.org/project/zhiqim_framework/zhiqim_httpd.htm
 *
 * Zhiqim Httpd is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
package org.zhiqim.httpd;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.zhiqim.httpd.model.HttpDomainPortPath;
import org.zhiqim.kernel.model.maps.HashMapSO;
import org.zhiqim.kernel.model.maps.HashMapSS;
import org.zhiqim.kernel.model.maps.MapSO;
import org.zhiqim.kernel.logging.Log;
import org.zhiqim.kernel.util.Amounts;
import org.zhiqim.kernel.util.Arrays;
import org.zhiqim.kernel.util.Asserts;
import org.zhiqim.kernel.util.Classes;
import org.zhiqim.kernel.util.Htmls;
import org.zhiqim.kernel.util.Ints;
import org.zhiqim.kernel.util.Longs;
import org.zhiqim.kernel.util.Strings;
import org.zhiqim.kernel.util.Urls;
import org.zhiqim.kernel.util.Validates;

/**
 * HTTP请求抽象类<br><br>
 * 1、把公共方法集中在抽象类<br>
 * 2、隔离内部方法（仅公开HttpRequest接口）<br>
 *
 * @version v1.5.0 @author zouzhigang 2018-9-12 新建与整理
 */
public abstract class HttpRequestAbs extends HttpRequestResponse
{
    //相关类
    protected HttpHeaderAbs header;
    protected HttpConnection conn;
    protected HttpServer server;
    protected HttpContext context;
    protected InputStream input;
    
    //四种键值表
    protected HashMapSS urlParamMap;
    protected HashMapSS cntParamMap;
    protected HashMapSS cookieMap;
    protected HashMapSO attributeMap;
    
    //内容流
    protected ByteArrayInputStream bis;
    
    //会话相关参数
    protected HttpSession session;
    protected String requestName;
    
    protected transient boolean hasForward;
    
    public HttpRequestAbs(HttpHeaderAbs header)
    {
        this.header = header;
        this.conn = header.getConnection();
        this.server = header.getServer();
        this.context = header.getContext();
        this.input = header.getInputStream();
        
        this.urlParamMap = new HashMapSS();
        this.cntParamMap = new HashMapSS();
        this.cookieMap = new HashMapSS();
        this.attributeMap = new HashMapSO();
    }
    
    public abstract boolean parseContent() throws IOException;
    
    /***********************************************************************/
    //解析请求头、内容、会话和证书等
    /***********************************************************************/
    
    /**
     * 解析内容类型、URL和Cookies等，该部分需在context设置之后操作，从parseHeader中分离出来，如果Context没找到，解析也没意义
     */
    public void parseHeaderByContextOK() throws HttpException
    {
        //第七步，检查最大内容长度，如果配置为0表示不检查，>0表示检查大小，默认8M
        int maxContentLength = context.getMaxContentLength();
        if (maxContentLength > 0)
        {
            int cLen = getContentLength();
            if (cLen > maxContentLength)
                throw new HttpException(_413_REQUEST_ENTITY_TOO_LARGE_);
        }
        
        //第八步，如果是HTTPS代理，有证书则还原证书，&设置客户端类型
        String sslCert = getHeader(_SSL_CERT_);
        if (Validates.isNotEmpty(sslCert))
            parseHeaderCert(sslCert);
        
        setAttribute(_ZHIQIM_CLIENT_TYPE_, getZhiqimClientType());
        
        //第九步，获取?之后的GET参数，并放置到getParams表中
        String queryString = header.getUri().getQuery();
        Urls.toMap(queryString, urlParamMap.instance(), header.getCharacterEncoding());
        
        //第十步，解析Cookies，并放置到cookies表中
        String cookieString = getHeader(_COOKIE_);
        if (Validates.isEmptyBlank(cookieString))
            return;
        
        HttpSessionManager sessionManager = context.getSessionManager();
        String sessionIdName = sessionManager == null?null:sessionManager.getSessionIdName();
        
        String cookiePrefix = getCookieForcePrefix();
        
        String[] cookieArray = cookieString.split(";");
        for (String cookieInfo : cookieArray)
        {
            String[] cookieNameValue = cookieInfo.split("=");
            if (cookieNameValue.length < 2)
                continue;
            
            String name = cookieNameValue[0].trim();
            String value = cookieNameValue[1].trim();
            if (sessionIdName == null || !sessionIdName.equals(name))
            {//无会话管理器的和不是sessionId的，相同名称的，目前取第一个，后面的不处理
                if (cookiePrefix != null)
                {//强制Cookie前缀的
                    if (!name.startsWith(cookiePrefix))
                        continue;
                    
                    name = name.substring(cookiePrefix.length());
                }
                
                if (!cookieMap.containsKey(name))
                {//强制规定UTF8编码，防止切换编码时读取异常
                    cookieMap.put(name, Urls.decodeUTF8(value));
                }
            }
            else
            {//有会话管理器的，且是sessionId的，判断是否符合本上下文环境的标记
                if (!Validates.isLen(value, 48, 48) || cookieMap.containsKey(name))
                {//长度不正确不处理，已存在，目前取第一个，后面不处理
                    continue;
                }
                
                String mark = value.substring(32);
                if (!sessionManager.chkSessionIdMark(mark, getDomainPortPath()))
                {//不符合本上下文环境的标记的不处理
                    continue;
                }
                
                cookieMap.put(name, value);
            }
        }
    }
    
    /**
     * 解析Get|Post|Cookie|Session信息,解析必须在context有效之后
     * 
     * @throws IOException IO异常
     */
    public void parseGetPostCookieSession() throws IOException
    {
        //第十一步，解析sesionId，组装session
        HttpSessionManager sessionManager = context.getSessionManager();
        if (sessionManager == null || !sessionManager.needSession(getPathInContext()))
            return;
        
        String sessionId = null;
        if (!context.isCookieUse())
            sessionId = urlParamMap.get(sessionManager.getSessionIdName());
        else
            sessionId = cookieMap.get(sessionManager.getSessionIdName());

        if (Validates.isEmptyBlank(sessionId) && context.isEncodeable())
            sessionId = header.getUri().getSessionId(context.getSessionManager().getSessionIdName());
        
        if (Validates.isEmptyBlank(sessionId) || !sessionManager.hasSession(sessionId))
        {//无会话标志或已过期或不一致，新建会话，创建时设置到会话管理器中，如果有浸入式更换sessionId,HttpResponse会重设置
            session = sessionManager.newSession(getDomainPortPath(), getRemoteAddr(), getUserAgent());
            sessionManager.setSession(session);
        }
        else
        {//有会话标志，设置最后访问时间
            session = sessionManager.getSessionByUpdate(sessionId, getRemoteAddr(), getUserAgent());
        }
    }
    
    /** 解析Nginx的证书 */
    private void parseHeaderCert(String sslCert)
    {
        try
        {
            sslCert = sslCert.replaceAll("\t", "\n");
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            
            ByteArrayInputStream bis = new ByteArrayInputStream(sslCert.getBytes());
            X509Certificate cert = (X509Certificate)cf.generateCertificate(bis);
            header.setCertificates(new X509Certificate[]{cert});
        }
        catch (CertificateException e)
        {
            //不处理
        }
    }
    
    /***********************************************************************/
    //获取监听、服务器、上下文环境、响应、会话相关
    /***********************************************************************/
    
    /** 获取连接中的日志对象 */
    public Log getLog()
    {
        return header.getLog();
    }
    
    /** 获取发送器 */
    public HttpSender getSender()
    {
        return header.getSender();
    }
    
    /** 获取请求编号 */
    public String getId()
    {
        return header.getId();
    }
    
    /** 获取产生时间戳 */
    public long getTimeMillis()
    {
        return header.getReceiveTimeMillis();
    }
    
    /** 获取对应的HTTP监听器 */
    public HttpListener getListener()
    {
        return header.getListener();
    }
    
    /** 获取对应的HTTP监听器监听端口 */
    public int getListenerPort()
    {
        return header.getListener().getPort();
    }
    
    /** 获取对应的HTTP服务器，注意只有host有效时才有 */
    public HttpServer getServer()
    {
        return server;
    }

    /** 获取HTTP连接 */
    public HttpConnection getConnection()
    {
        return header.getConnection();
    }

    /** 判断是否WebSocket */
    public boolean isWebSocket()
    {
        return header.isWebSocket();
    }

    /** 获取请求对应的响应对象 */
    public HttpResponse getResponse()
    {
        return header.getResponse();
    }
    
    /** 获取请求正在处理的上下文环境 */
    public HttpContext getContext()
    {
        return context;
    }
    
    /** 获取上下文环境路径 */
    public String getContextPath()
    {
        return context.getContextPath();
    }
    
    /** 获取上下文环境名称 */
    public String getContextName()
    {
        return context.getContextName();
    }
    
    /** 获取上下文环境版本 */
    public String getContextVersion()
    {
        return context.getContextVersion();
    }
    
    /** 设置上下文环境属性 */
    public void setContextAttribute(String key, Object value)
    {
        context.setAttribute(key, value);
    }
    
    /** 设置上下文环境属性值（通过类结构） */
    public <T> void setContextAttribute(Class<T> key, T value)
    {
        context.setAttribute(key, value);
    }
    
    /** 获取上下文环境属性值 */
    public Object getContextAttribute(String key)
    {
        return context.getAttribute(key);
    }
    
    /** 获取上下文环境属性值（通过类结构） */
    public <T> T getContextAttribute(Class<T> key)
    {
        return context.getAttribute(key);
    }
    
    /** 获取上下文环境属性值 */
    public Object getContextAttribute(String key, Object defaultValue)
    {
        return context.getAttribute(key, defaultValue);
    }
    
    /** 获取上下文环境属性值字符串 */
    public String getContextAttributeString(String key)
    {
        return context.getAttributeString(key);
    }
    
    /** 获取上下文环境属性值字符串 */
    public String getContextAttributeString(String key, String defaultValue)
    {
        return context.getAttributeString(key, defaultValue);
    }
    
    /** 获取上下文环境属性值整型 */
    public int getContextAttributeInt(String key)
    {
        return context.getAttributeInt(key);
    }
    
    /** 获取上下文环境属性值整型 */
    public int getContextAttributeInt(String key, int defaultValue)
    {
        return context.getAttributeInt(key, defaultValue);
    }
    
    /** 获取上下文环境属性值长整型 */
    public long getContextAttributeLong(String key)
    {
        return context.getAttributeLong(key);
    }
    
    /** 获取上下文环境属性值长整型 */
    public long getContextAttributeLong(String key, long defaultValue)
    {
        return context.getAttributeLong(key, defaultValue);
    }
    
    /** 获取上下文环境属性值布尔型 */
    public boolean getContextAttributeBoolean(String key, boolean defaultValue)
    {
        return context.getAttributeBoolean(key, defaultValue);
    }
    
    /** 判断上下文环境是否有属性值 */
    public boolean hasContextAttribute(String key)
    {
        return context.hasAttribute(key);
    }
    
    @Override/** 获取当前WEB目录根目录，注意没有/结尾 */
    public String getResourcePath()
    {
        return context.getResourcePath();
    }
    
    @Override/** 获取当前WEB目录路径转换成绝对路径 */
    public String getRealPath(String path)
    {
        return context.getRealPath(path);
    }
    
    @Override/** 获取上下文环境下绝对路径转为根环境下的绝对路径，如contextPath=/doc,path=/index.htm，得到/doc/index.htm */
    public String getRootPath(String path)
    {
        return context.getRootPath(path);
    }
    
    @Override
    public X509Certificate[] getCertificates()
    {
        return header.getCertificates();
    }
    
    /***********************************************************************/
    //请求状态处理和判断
    /***********************************************************************/
    
    @Override /** 是否可读，已读头部之后可读 */
    public boolean isRead()
    {
        return header.isRead();
    }
    
    @Override /** 是否可编辑，提交之前可编辑 */
    public boolean isEditable()
    {
        return header.isEditable();
    }
    
    @Override /** 是否已提交，提交之后不允许再操作request和response */
    public boolean isCommitted()
    {
        return header.isCommitted();
    }
    
    @Override /** 设置拦截中 */
    public void setStepInterceptor()
    {
        header.setStep(_08_INTERCEPTOR_);
    }
    
    @Override /** 设置Action处理中 */
    public void setStepAction()
    {
        header.setStep(_09_ACTION_);
    }

    @Override /** 获取当前处理环节 */
    public int getStep()
    {
        return header.getStep();
    }
    
    @Override /** 获取当前处理环节描述 */
    public String getStepDesc()
    {
        return header.getStepDesc();
    }
    
    /***********************************************************************/
    //设置和获取Session中属性
    /***********************************************************************/
    
    /** 获取请求用户名 */
    public String getRequestName()
    {
        return requestName;
    }
    
    /** 设置请求用户名 */
    public void setRequestName(String name)
    {
        this.requestName = name;
    }
    
    /** 获取请求对应的会话环境 */
    public HttpSession getSession()
    {
        return session;
    }
    
    /** 删除会话 */
    public void invalidateSession()
    {
        if (session != null)
            context.invalidateSession(session.getId());
        session = null;
    }
    
    /** 判断是否绑定了登录用户 */
    public boolean hasSessionUser()
    {
        return session != null && session.hasSessionUser();
    }
    
    /** 绑定会话登录用户 */
    public void bindSessionUser(HttpSessionUser sessionUser)
    {
        if (session != null){
            session.bindSessionUser(sessionUser);
        }
    }
    
    /** 解绑会话登录用户 */
    public void unbindSessionUser()
    {
        if (session != null){
            session.unbindSessionUser();
        }
    }
    
    /** 获取会话默认的用户信息 */
    public HttpSessionUser getSessionUser()
    {
        return session == null?null:session.getSessionUser();
    }
    
    /** 获取会话默认的登录名 */
    public String getSessionName()
    {
        return session == null?null:session.getSessionName();
    }
    
    /** 判断是否绑定了登录用户，且指定的会话用户类（HttpSessionUser子类） */
    public <T extends HttpSessionUser> boolean hasSessionUser(Class<T> clazz)
    {
        return session == null?false:session.hasSessionUser(clazz);
    }
    
    /** 获取会话用户信息，且指定的会话用户类（HttpSessionUser子类） */
    public <T extends HttpSessionUser> T getSessionUser(Class<T> clazz)
    {
        return session == null?null:session.getSessionUser(clazz);
    }
    
    /** 获取会话编号 */
    public String getSessionId()
    {
        assertSession();
        return session.getId();
    }
    
    /** 设置会话属性 */
    public void setSessionAttribute(String name, Object value)
    {
        assertSession();
        session.setAttribute(name, value);
    }
    
    /** 获取上下文环境属性值 */
    public Object getSessionAttribute(String name)
    {
        assertSession();
        return session.getAttribute(name);
    }
    
    /** 获取上下文环境属性值 */
    public Object getSessionAttribute(String name, Object defaultValue)
    {
        assertSession();
        return session.getAttribute(name, defaultValue);
    }
    
    /** 获取上下文环境属性值字符串 */
    public String getSessionAttributeString(String name)
    {
        assertSession();
        return session.getAttributeString(name);
    }
    
    /** 获取上下文环境属性值字符串 */
    public String getSessionAttributeString(String name, String defaultValue)
    {
        assertSession();
        return session.getAttributeString(name, defaultValue);
    }
    
    /** 获取上下文环境属性值整型 */
    public int getSessionAttributeInt(String name)
    {
        assertSession();
        return session.getAttributeInt(name);
    }
    
    /** 获取上下文环境属性值整型 */
    public int getSessionAttributeInt(String name, int defaultValue)
    {
        assertSession();
        return session.getAttributeInt(name, defaultValue);
    }
    
    /** 获取上下文环境属性值长整型 */
    public long getSessionAttributeLong(String name)
    {
        assertSession();
        return session.getAttributeLong(name);
    }
    
    /** 获取上下文环境属性值长整型 */
    public long getSessionAttributeLong(String name, long defaultValue)
    {
        assertSession();
        return session.getAttributeLong(name, defaultValue);
    }
    
    /** 判断会话是否有属性值 */
    public boolean hasSessionAttribute(String name)
    {
        assertSession();
        return session.hasAttribute(name);
    }
    
    /** 诊断会话是否存在 */
    private void assertSession()
    {
        Asserts.as(session != null?null:"[HttpSession]未创建或已删除，不允许调用");
    }

    /***********************************************************************/
    //获取Cookie数据或数组
    /***********************************************************************/
    
    /** 获取指定名称的Cookie值 */
    public String getCookie(String name)
    {
        return cookieMap.get(name);
    }
    
    /** 获取Cookie，如果没有值或值为空字符串返回缺省值 */
    public String getCookie(String name, String defaultValue)
    {
        String value = cookieMap.get(name);
        return Validates.isEmpty(value)?defaultValue:value;
    }
    
    /** 获取请求中Cookie数组 */
    public HttpCookie[] getCookies()
    {
        HttpCookie[] cookieArr = new HttpCookie[cookieMap.size()];
        int i = 0;
        for (Entry<String, String> entry : cookieMap.entrySet())
        {
            HttpCookie cookie = new HttpCookie(entry.getKey(), entry.getValue());
            cookieArr[i++] = cookie;
        }
        return cookieArr;
    }
    
    /** 删除请求中的Cookie值 */
    public void removeCookie(String name)
    {
        cookieMap.remove(name);
    }
    
    /** 增加Cookie到响应中 */
    public void addCookieToResponse(String name, String value)
    {
        getResponse().addCookie(name, value);
    }
    
    /** 增加Cookie到响应中 */
    public void addCookieToResponse(String name, String value, int seconds)
    {
        getResponse().addCookie(name, value, seconds);
    }
    
    /** 增加Cookie到响应中 */
    public void addCookieToResponse(HttpCookie cookie)
    {
        getResponse().addCookie(cookie);
    }
    
    /** 删除Cookie到响应中 */
    public void removeCookieToResponse(String name)
    {
        getResponse().removeCookie(name);
    }
    
    /** 获取Cookie强制前缀 */
    public String getCookieForcePrefix()
    {
        return getDomainPortPath().buildCookiePrefix(context.isCookieForce());
    }
    
    /***********************************************************************/
    //获取和判断请求行信息，包括协议、方法、版本、URI等
    /***********************************************************************/

    /** 获取请求行 */
    public String getHeaderLine()
    {
        return header.getHeaderLine();
    }
    
    /** 获取请求版本 */
    public String getVersion()
    {
        return header.getVersion();
    }
    
    /** 获取URI路径,以/开头，如/test.html，如果没有文件后缀则为'/' */
    public String getPath()
    {
        return header.getPath();
    }
    
    /** 获取请求方法 */
    public String getMethod()
    {
        return header.getMethod();
    }
    
    /** 是否是HEAD方法 */
    public boolean isMethodHead()
    {
        return header.isMethodHead();
    }
    
    /** 是否是OPTIONS方法 */
    public boolean isMethodOptions()
    {
        return header.isMethodOptions();
    }
    
    /** 是否是PUT方法 */
    public boolean isMethodPut()
    {
        return header.isMethodPut();
    }
    
    /** 是否是DELETE方法 */
    public boolean isMethodDelete()
    {
        return header.isMethodPut();
    }
    
    /** 是否是GET方法 */
    public boolean isMethodGet()
    {
        return header.isMethodGet();
    }
    
    /** 是否是POST方法 */
    public boolean isMethodPost()
    {
        return header.isMethodPost();
    }
    
    /** 判断是否需要响应内容 */
    public boolean isMethodResponseContent()
    {
        return header.isMethodResponseContent();
    }
    
    /** 获取客户端IP地址 */
    public String getRemoteAddr()
    {
        return header.getRemoteAddr();
    }
    
    /** 获取客户端端口 */
    public int getRemotePort()
    {
        return header.getRemotePort();
    }
    
    /** 获取URI信息 */
    public String getRequestURI()
    {
        return header.getUri().toString();
    }
    
    /** 获取URL信息 */
    public String getRequestURL()
    {
        String scheme = getScheme();
        String hostPort = getHostPort();
        
        return new StringBuilder()
            .append(scheme).append("://")
            .append(hostPort).append(getRequestURI())
            .toString();
    }
    
    /** 获取查询串 */
    public String getQueryString()
    {
        return header.getQueryString();
    }
    
    /** 获取协议格式 */
    public String getScheme()
    {
        return header.getScheme();
    }
    
    /** 获取HOST:PORT */
    public String getHostPort()
    {
        return header.getHostPort();
    }
    
    /** 获取端口信息 */
    public int getPort()
    {
        return header.getPort();
    }
    
    /** 获取URI虚拟主机信息 */
    public String getHostOnly()
    {
        return header.getHostOnly();
    }
    
    /** 获取URI虚拟目录信息 */
    public String getVirtualDirectory()
    {
        return header.getVirtualDirectory();
    }
    
    @Override
    public HttpDomainPortPath getDomainPortPath()
    {
        return header.getDomainPortPath();
    }
    
    /***********************************************************************/
    //获取和判断请求头部信息
    /***********************************************************************/
    
    /** 获取头部信息 */
    public HashMapSS getHeaderMap()
    {
        return header.getHeaderMap();
    }
    
    /** 获取消息头名称迭代器 */
    public Iterator<String> getHeaderNames()
    {
        return header.getHeaderMap().keySet().iterator();
    }
    
    /** 获取请求头字符串 */
    public String getHeaderString()
    {
        StringBuilder strb = new StringBuilder();
        for (Map.Entry<String, String> entry : header.getHeaderMap().entrySet())
        {
            strb.append(entry.getKey()).append(_EQUAL_).append(entry.getValue()).append(_BR_);
        }
        return strb.toString();
    }
    
    /** 获取请求头属性 */
    public String getHeader(String key)
    {
        return header.getHeader(key.toLowerCase());
    }
    
    /** 获取请求头属性 */
    public String getHeader(String key, String defaultValue)
    {
        String value = getHeader(key);
        return Validates.isEmptyBlank(value)?defaultValue:value;
    }
    
    /** 获取请求头属性整型值 */
    public int getHeaderInt(String key)
    {
        return getHeaderInt(key, -1);
    }
    
    /** 获取请求头属性整型值 */
    public int getHeaderInt(String key, int defaultValue)
    {
        String value = getHeader(key);
        return Validates.isInteger(value)?Ints.toInt(value):defaultValue;
    }
    
    /** 获取请求头属性长整型值 */
    public long getHeaderLong(String key)
    {
        return getHeaderLong(key, -1);
    }
    
    /** 获取请求头属性长整型值 */
    public long getHeaderLong(String key, long defaultValue)
    {
        String value = getHeader(key);
        return Validates.isInteger(value)?Longs.toLong(value):defaultValue;
    }
    
    /** 获取请求内容长度 */
    public int getContentLength()
    {
        String sLen = getHeader(_CONTENT_LENGTH_);
        if (!Validates.isInteger(sLen))
            return 0;
        return Integer.parseInt(sLen);
    }

    /** 获取请求内容类型 */
    public String getContentType()
    {
        return getHeader(_CONTENT_TYPE_);
    }
    
    /** 获取请求要求的类型 */
    public String getMimeType()
    {
        return header.getMimeType();
    }
    
    /** 判断是否表单提交 */
    public boolean isMimeForm()
    {
        return header.isMimeForm();
    }
    
    /** 判断是否文本请求 */
    public boolean isMimeTextPlain()
    {
        return header.isMimeTextPlain();
    }
    
    /** 设置请求的编码格式 */
    public void setCharacterEncoding(String characterEncoding)
    {
        header.setCharacterEncoding(characterEncoding);
    }

    /** 获取请求要求的编码,如果未设置默认UTF-8 */
    public String getCharacterEncoding()
    {
        return header.getCharacterEncoding();
    }
    
    /** 获取请求头中的编码,如果未设置默认null */
    public String getCharacterEncodingHeader()
    {
        return header.getCharacterEncodingHeader();
    }
    
    /** 获取组装内容类型 */
    public String getContentTypeMimeEncoding()
    {
        return header.getMimeType() + "; charset=" + header.getCharacterEncoding();
    }
    
    /** 获取来源地址 */
    public String getReferer()
    {
        return getHeader(_REFERER_);
    }
    
    /** 获取知启蒙定义的客户端类型（mobile|www），该值也会设置到request的属性中 */
    public String getZhiqimClientType()
    {
        return isMobile()?_MOBILE_:_WWW_;
    }
    
    /** 是否请求内容GZIP */
    public boolean isRequestGZip()
    {
        return header.isRequestGZip();
    }
    
    /** 是否响应支持GZIP */
    public boolean isResponseGZip()
    {
        return header.isResponseGZip();
    }
    
    /** 是否是异isXMLHttpRequest */
    public boolean isXMLHttpRequest()
    {
        return _XML_HTTP_REQUEST_.equalsIgnoreCase(getHeader(_X_REQUESTED_WITH_));
    }
    
    /** 获取浏览器终端标识 */
    public String getUserAgent()
    {
        return header.getUserAgent();
    }

    /** 是否手机端（含微信客户端） */
    public boolean isMobile()
    {
        String userAgent = getUserAgent();
        if (Validates.isEmpty(userAgent))
            return false;
        
        return userAgent.toLowerCase().contains(_MOBILE_) || userAgent.toLowerCase().contains(_MICROMESSAGER_);
    }
    
    /** 是否微信 */
    public boolean isMicroMessenger()
    {
        String userAgent = getUserAgent();
        if (Validates.isEmpty(userAgent))
            return false;
        
        return userAgent.toLowerCase().contains(_MICROMESSAGER_);
    }
    
    /** 是否IE浏览器 */
    public boolean isIE()
    {
        String userAgent = getUserAgent();
        if (Validates.isEmpty(userAgent))
            return false;
        
        userAgent = userAgent.toLowerCase();
        return userAgent.contains(_MSIE_) || userAgent.contains(_MSTRIDENT_);
    }
    
    /** 是否火狐浏览器 */
    public boolean isFirefox()
    {
        String userAgent = getUserAgent();
        if (Validates.isEmpty(userAgent))
            return false;
        
        return userAgent.toLowerCase().contains(_FIREFOX_);
    }
    
    /** 是否是Webkit浏览器 */
    public boolean isWebkit()
    {
        String userAgent = getUserAgent();
        if (Validates.isEmpty(userAgent))
            return false;
        
        return userAgent.toLowerCase().contains(_WEBKIT_);
    }
    
    /** 是否是chrome浏览器 */
    public boolean isChrome()
    {
        String userAgent = getUserAgent();
        if (Validates.isEmpty(userAgent))
            return false;
        
        return userAgent.toLowerCase().contains(_CHROME_);
    }
    
    /** 是否是Safari浏览器 */
    public boolean isSafari()
    {
        String userAgent = getUserAgent();
        if (Validates.isEmpty(userAgent))
            return false;
        
        userAgent = userAgent.toLowerCase();
        return userAgent.contains(_SAFARI_) && !userAgent.contains(_CHROME_);
    }
    
    /***********************************************************************/
    //获取请求中URL匹配的参数
    /***********************************************************************/
    
    /** 获取通配符参数列表 */
    @SuppressWarnings("unchecked")
    public List<String> getParameterMatch()
    {
        return (List<String>)getAttribute(_HTTP_REQUEST_PARAM_MATCH_);
    }
    
    /**
     * 获取通配符参数值
     * 
     * @param index 参数匹配位置
     * @return      有过滤的参数值
     */
    public String getParameterMatch(int index)
    {
        return getParameterMatch().get(index);
    }
    
    /**
     * 获取通配符参数值
     * 
     * @param index 参数匹配位置
     * @return      有过滤的参数值
     */
    public long getParameterMatchLong(int index)
    {
        return Longs.toLong(getParameterMatch().get(index));
    }
    
    /**
     * 获取通配符参数值
     * 
     * @param index 参数匹配位置
     * @return      有过滤的参数值
     */
    public int getParameterMatchInt(int index)
    {
        return Ints.toInt(getParameterMatch().get(index));
    }
    
    /***********************************************************************/
    //获取请求中URL和Content中的数据，包括URL和Content中的数据
    /***********************************************************************/
    
    /**
     * 获取参数哈唏表（未安全过滤），先填入url中参数表，再用content参数表覆盖，得到全部参数表
     * 
     * @return  得到全部参数表
     */
    public HashMapSS getParameterMap()
    {
        HashMapSS paramMap = new HashMapSS();
        paramMap.putAll(urlParamMap);
        paramMap.putAll(cntParamMap);
        return paramMap;
    }
    
    /** 判断是否有参数 */
    public boolean hasParameter(String name)
    {
        return cntParamMap.containsKey(name) || urlParamMap.containsKey(name);
    }
    
    /**
     * 获取参数值并安全过滤，优先检查content中是否存在，如果不存在才检查url中
     * 
     * @param name  参数名
     * @return      有过滤的参数值
     */
    public String getParameter(String name)
    {
        return Htmls.filterAll(hasParameterOnCNT(name)?cntParamMap.get(name):urlParamMap.get(name));
    }
    
    /**
     * 获取动态参数对象信息
     * 
     * @param clazz     类结构
     * @return          返回赋值后的对象
     * @throws          异常需要捕捉
     */
    public <T> T getParameter(Class<T> clazz) throws IllegalAccessException
    {
        return Classes.newInstance(clazz, getParameterMap());
    }
    
    /**
     * 获取参数值（未过滤），优先检查content中是否存在，如果不存在才检查url中
     * 
     * @param name  参数名
     * @return      有过滤的参数值
     */
    public String getParameterNoFilter(String name)
    {
        return Strings.trim(hasParameterOnCNT(name)?cntParamMap.get(name):urlParamMap.get(name));
    }
    
    /**
     * 获取参数值并安全过滤，优先检查content中是否存在，如果不存在才检查url中，都不存在或值为空白返回缺省值
     * 
     * @param name          参数名
     * @param defaultValue  不存在或为空时的缺省值
     * @return              有过滤的参数值
     */
    public String getParameter(String name, String defaultValue)
    {
        String value = getParameter(name);
        return Validates.isEmpty(value)?defaultValue:value;
    }
    
    /**
     * 获取参数值（未过滤），优先检查content中是否存在，如果不存在才检查url中，都不存在或值为空白返回缺省值
     * 
     * @param name          参数名
     * @param defaultValue  不存在或为空时的缺省值
     * @return              有过滤的参数值
     */
    public String getParameterNoFilter(String name, String defaultValue)
    {
        String value = getParameterNoFilter(name);
        return Validates.isEmpty(value)?defaultValue:value;
    }
    
    /**
     * 获取参数整型，非整型返回-1的值
     * 
     * @param name          参数名
     * @return              整数值
     */
    public int getParameterInt(String name)
    {
        return Ints.toInt(getParameter(name), -1);
    }
    
    /**
     * 获取参数整型，非整型返回缺省值
     * 
     * @param name          参数名
     * @param defaultValue  缺省值
     * @return              整数值
     */
    public int getParameterInt(String name, int defaultValue)
    {
        return Ints.toInt(getParameter(name), defaultValue);
    }
    
    /**
     * 获取参数长整型，非整型返回-1的值
     * 
     * @param name          参数名
     * @return              长整型
     */
    public long getParameterLong(String name)
    {
        return Longs.toLong(getParameter(name), -1);
    }
    
    /**
     * 获取参数长整型，非整型返回-1的值
     * 
     * @param name          参数名
     * @param defaultValue  缺省值
     * @return              长整型
     */
    public long getParameterLong(String name, long defaultValue)
    {
        return Longs.toLong(getParameter(name), defaultValue);
    }
    
    /**
     * 获取参数布尔型，非布尔型返回null的值
     * 
     * @param name          参数名
     * @return              布尔型
     */
    public Boolean getParameterBool(String name)
    {
        String value = getParameter(name);
        if (Validates.isEmptyBlank(value))
            return null;
        
        if (_TRUE_.equalsIgnoreCase(value))
            return true;
        else if (_FALSE_.equalsIgnoreCase(value))
            return false;
        else
            return null;
    }
    
    /**
     * 获取参数布尔型，!="true"（equalsIgnoreCase）的返回false
     * 
     * @param name          参数名
     * @return              布尔型
     */
    public boolean getParameterBoolean(String name)
    {
        return _TRUE_.equalsIgnoreCase(getParameter(name));
    }
    
    /**
     * 获取参数布尔型，!="true" && !="false"(equalsIgnoreCase)返回defaultValue
     * 
     * @param name          参数名
     * @param defaultValue  缺省值
     * @return              布尔型
     */
    public boolean getParameterBoolean(String name, boolean defaultValue)
    {
        String value = getParameter(name);
        if (Validates.isEmptyBlank(value))
            return defaultValue;
        
        if (_TRUE_.equalsIgnoreCase(value))
            return true;
        else if (_FALSE_.equalsIgnoreCase(value))
            return false;
        else
            return defaultValue;
    }
    
    /**
     * 获取两位小数的金额值，格式不对返回-1
     * 
     * @param name          参数名
     * @return              金额值，如果从请求中获取的格式不对，则返回-1
     */
    public int getParameterAmount2R(String name)
    {
        return Amounts.toFen(getParameter(name), -1);
    }
    
    /**
     * 获取两位小数的金额值
     * 
     * @param name          参数名
     * @param defaultValue  缺省值
     * @return              金额值，如果从请求中获取的格式不对，则返回缺省值
     */
    public int getParameterAmount2R(String name, int defaultValue)
    {
        return Amounts.toFen(getParameter(name), defaultValue);
    }
    
    /**
     * 获取两位小数的金额值长整型，格式不对返回-1
     * 
     * @param name          参数名
     * @param defaultValue  缺省值
     * @return              金额值，如果从请求中获取的格式不对，则返回-1
     */
    public long getParameterAmount2RLong(String name)
    {
        return Amounts.toFen(getParameter(name), -1L);
    }
    
    /**
     * 获取两位小数的金额值长整型
     * 
     * @param name          参数名
     * @param defaultValue  缺省值
     * @return              金额值，如果从请求中获取的格式不对，则返回缺省值
     */
    public long getParameterAmount2R(String name, long defaultValue)
    {
        return Amounts.toFen(getParameter(name), defaultValue);
    }
    
    /**
     * 获取参数值并安全过滤，并转化为数组（逗号格式），优先检查content中是否存在，如果不存在才检查url中
     * 
     * @param name  参数名
     * @return      有过滤后的逗号格式数组值
     */
    public String[] getParameterValues(String name)
    {
        String value = getParameter(name);
        return (value == null)?null:Arrays.toStringArray(value);
    }
    
    /**
     * 获取参数值（未过滤），并转化为数组（逗号格式），优先检查content中是否存在，如果不存在才检查url中
     * 
     * @param name  参数名
     * @return      有过滤后的逗号格式数组值
     */
    public String[] getParameterValuesNoFilter(String name)
    {
        String value = getParameterNoFilter(name);
        return (value == null)?null:Arrays.toStringArray(value);
    }
    
    /***********************************************************************/
    //获取请求中URL中的数据
    /***********************************************************************/
    
    /** 获取URL参数哈唏表（值未过滤） */
    public HashMapSS getParameterMapOnURL()
    {
        HashMapSS paramMap = new HashMapSS(urlParamMap.size());
        paramMap.putAll(urlParamMap);
        return paramMap;
    }
    
    /** 判断是否有URL参数 */
    public boolean hasParameterOnURL(String name)
    {
        return urlParamMap.containsKey(name);
    }
    
    /** 获取URL参数值，并安全过滤 */
    public String getParameterOnURL(String name)
    {
        return Htmls.filterAll(urlParamMap.get(name));        
    }
    
    /** 获取URL参数值，并安全过滤 */
    public String getParameterOnURL(String name, String defaultValue)
    {
        String value = getParameterOnURL(name);
        return Validates.isEmptyBlank(value)?defaultValue:value;
    }
    
    /** 获取URL参数值(未过滤) */
    public String getParameterNoFilterOnURL(String name)
    {
        return Strings.trim(urlParamMap.get(name));        
    }
    
    /** 获取URL参数值(未过滤) */
    public String getParameterNoFilterOnURL(String name, String defaultValue)
    {
        String value = getParameterNoFilterOnURL(name);
        return Validates.isEmptyBlank(value)?defaultValue:value;
    }
    
    /** 获取URL参数整型，非整型返回-1的值 */
    public int getParameterIntOnURL(String name)
    {
        return Ints.toInt(getParameterOnURL(name), -1);
    }
    
    /** 获取URL参数整型，非整型返回缺省值 */
    public int getParameterIntOnURL(String name, int defaultValue)
    {
        return Ints.toInt(getParameterOnURL(name), defaultValue);
    }
    
    /** 获取URL参数长整型，非整型返回-1的值 */
    public long getParameterLongOnURL(String name)
    {
        return Longs.toLong(getParameterOnURL(name), -1);
    }
    
    /** 获取URL参数长整型，非整型返回-1的值 */
    public long getParameterLongOnURL(String name, long defaultValue)
    {
        return Longs.toLong(getParameterOnURL(name), defaultValue);
    }
    
    /** 获取两位小数的金额值 */
    public int getParameterAmount2ROnURL(String name, int defaultValue)
    {
        return Amounts.toFen(getParameterOnURL(name), defaultValue);
    }
    
    /** 获取两位小数的金额值长整型 */
    public long getParameterAmount2ROnURL(String name, long defaultValue)
    {
        return Amounts.toFen(getParameterOnURL(name), defaultValue);
    }

    /** 获取URL参数值数组，并安全过滤 */
    public String[] getParameterValuesOnURL(String name)
    {
        String value = getParameterOnURL(name);
        return (value == null)?null:Arrays.toStringArray(value);
    }
    
    /** 设置参数值到URL表中 */
    public void setParameterOnURL(String name, Object value)
    {
        urlParamMap.put(name, value == null?null:String.valueOf(value));
    }
    
    /** 设置参数值到CNT表中 */
    public void setParameterOnCNT(String name, Object value)
    {
        cntParamMap.put(name, value == null?null:String.valueOf(value));
    }
    
    /***********************************************************************/
    //获取请求中Content中的数据
    /***********************************************************************/

    /** 获取内容输入流 */
    public InputStream getInputStream()
    {
        return bis;
    }
    
    /** 获取内容输入流字符串 */
    public String getInputStreamString()
    {
        int len = getContentLength();
        if (len == 0)
            return "";
        
        try
        {
            byte[] buffer = new byte[len];
            bis.read(buffer);
            return new String(buffer, getCharacterEncoding());
        }
        catch (Exception e)
        {
            throw Asserts.exception("解析请求内容时异常："+e.getMessage());
        }
    }
    
    /** 获取内容参数哈唏表（值未过滤） */
    public HashMapSS getParameterMapOnCNT()
    {
        HashMapSS paramMap = new HashMapSS(cntParamMap.size());
        paramMap.putAll(cntParamMap);
        return paramMap;
    }
    
    /** 判断是否有内容参数 */
    public boolean hasParameterOnCNT(String name)
    {
        return cntParamMap.containsKey(name);
    }
    
    /** 获取内容参数值，并安全过滤 */
    public String getParameterOnCNT(String name)
    {
        return Htmls.filterAll(cntParamMap.get(name));        
    }
    
    /** 获取内容参数值，并安全过滤 */
    public String getParameterOnCNT(String name, String defaultValue)
    {
        String value = getParameterOnCNT(name);
        return Validates.isEmptyBlank(value)?defaultValue:value;
    }
    
    /** 获取内容参数值(未过滤) */
    public String getParameterNoFileterOnCNT(String name)
    {
        return Strings.trim(cntParamMap.get(name));
    }
    
    /** 获取内容参数值(未过滤) */
    public String getParameterNoFileterOnCNT(String name, String defaultValue)
    {
        String value = getParameterNoFileterOnCNT(name);
        return Validates.isEmptyBlank(value)?defaultValue:value;
    }
    
    /** 获取内容参数整型，非整型返回-1的值 */
    public int getParameterIntOnCNT(String name)
    {
        return Ints.toInt(getParameterOnCNT(name), -1);
    }
    
    /** 获取内容参数整型，非整型返回缺省值 */
    public int getParameterIntOnCNT(String name, int defaultValue)
    {
        return Ints.toInt(getParameterOnCNT(name), defaultValue);
    }
    
    /** 获取内容参数长整型，非整型返回-1的值 */
    public long getParameterLongOnCNT(String name)
    {
        return Longs.toLong(getParameterOnCNT(name), -1);
    }
    
    /** 获取内容参数长整型，非整型返回-1的值 */
    public long getParameterLongOnCNT(String name, long defaultValue)
    {
        return Longs.toLong(getParameterOnCNT(name), defaultValue);
    }
    
    /** 获取内容参数两位小数的金额值 */
    public int getParameterAmount2ROnCNT(String name, int defaultValue)
    {
        return Amounts.toFen(getParameterOnCNT(name), defaultValue);
    }
    
    /** 获取内容参数两位小数的金额值长整型 */
    public long getParameterAmount2ROnCNT(String name, long defaultValue)
    {
        return Amounts.toFen(getParameterOnCNT(name), defaultValue);
    }
    
    /** 获取内容参数值数组 */
    public String[] getParameterValuesOnCNT(String name)
    {
        String value = getParameterOnCNT(name);
        return (value == null)?null:Arrays.toStringArray(value);
    }
    
    /***********************************************************************/
    //设置和获取请求中属性
    /***********************************************************************/
    
    /** 获取属性列表 */
    public MapSO getAttributeMap()
    {
        return attributeMap;
    }
    
    /** 获取属性值 */
    public Object getAttribute(String name)
    {
        return attributeMap.get(name);
    }
    
    /** 获取属性值，如果无该属性则返回缺省值 */
    public Object getAttribute(String name, Object defaultValue)
    {
        return (hasAttribute(name))?getAttribute(name):defaultValue;
    }
    
    /** 获取属性值字符串*/
    public String getAttributeString(String name)
    {
        return getAttributeString(name, null);
    }
    
    /** 获取属性值字符串，如果无该属性则返回缺省值*/
    public String getAttributeString(String name, String defaultValue)
    {
        Object value = attributeMap.get(name);
        return (value == null)?defaultValue:String.valueOf(value);
    }
    
    /** 获取属性值整型*/
    public int getAttributeInt(String name)
    {
        return Ints.toInt(attributeMap.get(name), -1);
    }
    
    /** 获取属性值整型，如果无该属性则返回缺省值*/
    public int getAttributeInt(String name, int defaultValue)
    {
        return Ints.toInt(attributeMap.get(name), defaultValue);
    }
    
    /** 获取属性值整型*/
    public long getAttributeLong(String name)
    {
        return Longs.toLong(attributeMap.get(name), -1);
    }
    
    /** 获取属性值整型，如果无该属性则返回缺省值*/
    public long getAttributeLong(String name, int defaultValue)
    {
        return Longs.toLong(attributeMap.get(name), defaultValue);
    }
    
    /** 判断属性是否存在 */
    public boolean hasAttribute(String name)
    {
        return attributeMap.containsKey(name);
    }
    
    /** 设置属性 */
    public void setAttribute(String name, Object value)
    {
        attributeMap.put(name, value);
    }
    
    /***********************************************************************/
    //获取请求/会话/上下文环境中嵌套属性，有则返回
    /***********************************************************************/
    
    /** 获取属性值 */
    public Object getNestAttribute(String name)
    {
        boolean exists = hasAttribute(name);
        if (exists)
            return getAttribute(name);
        
        exists = hasSessionAttribute(name);
        if (exists)
            return getSessionAttribute(name);
        
        exists = hasContextAttribute(name);
        return exists?getContextAttribute(name):null;
    }
    
    /** 获取属性值，如果无该属性则返回缺省值 */
    public Object getNestAttribute(String name, Object defaultValue)
    {
        return hasNestAttribute(name)?getNestAttribute(name):defaultValue;
    }
    
    /** 获取属性值字符串 */
    public String getNestAttributeString(String name)
    {
        return getNestAttributeString(name, null);
    }
    
    /** 获取属性值字符串，如果无该属性则返回缺省值 */
    public String getNestAttributeString(String name, String defaultValue)
    {
        if (!hasNestAttribute(name))
            return defaultValue;
        
        Object val = getNestAttribute(name);
        return (val == null)?null:Strings.valueOf(val);
    }
    
    /** 获取属性值整型 */
    public int getNestAttributeInt(String name)
    {
        return getNestAttributeInt(name, -1);
    }
    
    /** 获取属性值整型，如果无该属性则返回缺省值 */
    public int getNestAttributeInt(String name, int defaultValue)
    {
        return Ints.toInt(getNestAttribute(name), defaultValue);
    }
    
    /** 获取属性值整型 */
    public long getNestAttributeLong(String name)
    {
        return getNestAttributeLong(name, -1);
    }
    
    /** 获取属性值整型，如果无该属性则返回缺省值 */
    public long getNestAttributeLong(String name, int defaultValue)
    {
        return Longs.toLong(getNestAttribute(name), defaultValue);
    }
    
    /** 判断属性是否存在 */
    public boolean hasNestAttribute(String name)
    {
        boolean exists = hasAttribute(name);
        if (exists)
            return true;
        
        exists = hasSessionAttribute(name);
        if (exists)
            return true;
        
        return hasContextAttribute(name);
    }
    
    /***********************************************************************/
    //从请求中设置属性到响应中
    /***********************************************************************/
    
    /** 设置响应头不缓存 */
    public void setResponseNoCache()
    {
        HttpResponse response = getResponse();
        response.setHeader(_PRAGMA_,        _NO_CACHE_);
        response.setHeader(_CACHE_CONTROL_, _NO_CACHE_MUST_RV_NO_STORE_);
        response.setHeader(_P3P_,           _P3P_CP_CAO_PSA_OUR_);
        response.setDateHeader(_EXPIRES_,   0);
    }
    
    /** 设置响应头私有缓存 */
    public void setResponsePrivateCache()
    {
        getResponse().setCacheControlPrivate();
    }
    
    /** 设置响应为UTF8编码*/
    public void setResponseEncodingUTF8()
    {
        HttpResponse response = getResponse();
        response.setCharacterEncoding(_UTF_8_);
    }
    
    /***********************************************************************/
    //内部重定向请求和上下文环境下绝对路径
    /***********************************************************************/
    
    /**
     * 内部重定向请求，支持从一个Action内部转到新的Action处理
     * 
     * @param pathInContext     上下文内部路径，即对应的新Action的配置路径
     * @throws IOException      IO异常
     * @throws HttpException    HTTP异常
     */
    public void forwardTo(String pathInContext) throws HttpException, IOException
    {
        int ind = pathInContext.indexOf("?");
        if (ind == -1){
            header.setPathInContext(pathInContext);
        }else{
            Map<String, String> paramMap = Urls.toMapNoEncoded(pathInContext);
            this.urlParamMap.putAll(paramMap);
            header.setPathInContext(pathInContext.substring(0, ind));
        }
        
        HttpResponse response = getResponse();
        HttpContext context = getContext();
        context.handle(this, response);
        
        //处理完标志为已重定向，并立即提交，不再处理后续数据传送
        hasForward = true;
        response.commit();
    }
    
    /** 是否已标志为已内部重定向 */
    public boolean hasForward()
    {
        return hasForward;
    }
    
    /** 获取上下文环境下绝对路径 */
    public String getPathInContext()
    {
        return header.getPathInContext();
    }
    
    /** 获取远程方法调用时配置的路径 */
    public String getPathInRMI()
    {
        return header.getPathInRMI();
    }
    
    /** 设置远程方法调用时配置的路径 */
    public void setPathInRMI(String pathInRMI)
    {
        header.setPathInRMI(pathInRMI);
    }
    
    /** 获取资源文件下绝对路径 */
    public String getPathOnResource()
    {
        return header.getPathOnResource();
    }
    
    /** 设置资源文件下绝对路径 */
    public void setPathOnResource(String pathOnResource)
    {
        header.setPathOnResource(pathOnResource);
    }
    
    /***********************************************************************/
    //toString & destroy
    /***********************************************************************/
    
    public String toString()
    {
        return header.toString();
    }
    
    /** 销毁 */
    public void destroy()
    {
        super.destroy();
        
        if (bis != null)
        {
            try{bis.close();}catch(IOException e){}
            bis = null;
        }
        
        //map
        if (urlParamMap != null)
        {
            urlParamMap.clear();
            urlParamMap = null;
        }
        if (cntParamMap != null)
        {
            cntParamMap.clear();
            cntParamMap = null;
        }
        if (cookieMap != null)
        {
            cookieMap.clear();
            cookieMap = null;
        }
        if (attributeMap != null)
        {
            attributeMap.clear();
            attributeMap = null;
        }
        
        //引用置空
        header = null;
        server = null;
        conn = null;
        context = null;
        input = null;
        session = null;
    }
}
